﻿using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    /// <summary>
    /// C# 5.0中的async和await (.NET 4.5中，微软又提出了async和await两个关键字来支持异步编程。)
    /// </summary>
    public partial class Form_Async_Await : Form
    {
        long totalSize = 0;
        string downloadPath = null;
        int DownloadSize = 0;
        FileStream filestream;

        CancellationTokenSource cts = null;
        Task task = null;

        public Form_Async_Await()
        {
            InitializeComponent();
            txbUrl.Text = "http://k3.chunk.cn:8053/POSReport20/File/%E5%8D%9520240101_1217_1218171322608.xlsx";

            btnPause.Enabled = false;

            GetTotalSize();
            downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.txbUrl.Text.Trim());
            if (File.Exists(downloadPath))
            {
                FileInfo fileInfo = new FileInfo(downloadPath);
                DownloadSize = (int)fileInfo.Length;
                progressBar1.Value = (int)((float)DownloadSize / (float)totalSize * 100);
            }
        }

        private async Task btnDownLoad_ClickAsync(object sender, EventArgs e)
        {
            filestream = new FileStream(downloadPath, FileMode.OpenOrCreate);
            this.btnDownLoad.Enabled = false;
            this.btnPause.Enabled = true;

            filestream.Seek(DownloadSize, SeekOrigin.Begin);

            cts = new CancellationTokenSource();

            await DownLoadFileAsync(txbUrl.Text.Trim(), cts.Token, new Progress<int>(p => progressBar1.Value = p));
        }

        private void btnPause_Click(object sender, EventArgs e)
        {
            cts.Cancel(); // 发出一个取消请求
        }

        // C# 5.0使用async和await的方式来实现文件下载
        private async Task DownLoadFileAsync(string url, CancellationToken ct, IProgress<int> progress)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            Stream responseStream = null;
            int bufferSize = 2048;
            byte[] bufferBytes = new byte[bufferSize];

            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                if (DownloadSize != 0)
                {
                    request.AddRange(DownloadSize);
                }

                response = (HttpWebResponse)await request.GetResponseAsync();
                responseStream = response.GetResponseStream();
                int readSize = 0;
                while (true)
                {
                    if (ct.IsCancellationRequested == true)
                    {
                        MessageBox.Show(string.Format("下载暂停，下载的文件地址为：{0}\n 已经下载的字节数为: {1}字节", downloadPath, DownloadSize));
                        response.Close();
                        filestream.Close();

                        this.btnDownLoad.Enabled = true;
                        this.btnPause.Enabled = false;
                        break;
                    }

                    readSize = await responseStream.ReadAsync(bufferBytes, 0, bufferBytes.Length);
                    if (readSize > 0)
                    {
                        DownloadSize += readSize;
                        int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100);
                        await filestream.WriteAsync(bufferBytes, 0, readSize);

                        progress.Report(percentComplete); // 报告进度
                    }
                    else
                    {
                        MessageBox.Show(String.Format("下载已完成，下载的文件地址为：{0}，文件的总字节数为: {1}字节", downloadPath, totalSize));

                        this.btnDownLoad.Enabled = false;
                        this.btnPause.Enabled = false;
                        response.Close();
                        filestream.Close();
                        break;
                    }
                }
            }
            catch (AggregateException ex)
            {
                ex.Handle(e => e is OperationCanceledException);
            }
        }

        private void GetTotalSize() //获得文件的总大小
        {
            HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());
            HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse();
            totalSize = response.ContentLength;
            response.Close();
        }

        // 从以上代码可以看出，使用async和await关键字实现的异步编程与用基于任务的异步模式的实现非常类似。此时，我们不再需要单独创建一个任务然后调用Start方法来启动它了，所有的一切都封装在了async和await关键字中。

        // 另外，在async和await关键字的帮助下，你可以使用同步编程的思考方式来实现异步编程。
        // 如在这段代码的DownLoadFileAsync异步方法实现中，除了在方法头多了一个async关键字，以及在方法体处多了一个await关键字外，其他都与同步方法的实现相同。
        // 并且在该方法调用的btnDownLoad_Click处理函数中，也是通过await关键字的方式进行直接调用的，没有其他额外代码。

        // 第三，这段代码中也不存在获取同步上下文对象的代码。async和await关键字不会让调用方法运行在新线程中，而是将方法分割成多个片段（片段的界限出现在方法内部使用await关键字的位置处），并使其中一些片段可以异步运行。
        // await关键字处的代码片段是在线程池线程上运行的，而整个方法的调用却是同步的。所以，使用此方式编程不用考虑跨线程访问UI控件的问题，从而大大降低了异步编程的出错率。
    }
}
